package com.qianya.weishi_server.config.jwt;



import com.qianya.weishi_server.common.result.GenericErrorCode;
import com.qianya.weishi_server.common.exception.GenericException;
import io.jsonwebtoken.*;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author LLL
 */
public class JwtUtil {

    /**
     * 生成Token
     */
    public static String createToken(Map<String, Object> claim, String subject, JwtProperty property) {
        LocalDateTime nowTime = LocalDateTime.now();
        JwtBuilder builder = Jwts.builder()
                .setClaims(claim)
                .setSubject(subject)
                .setIssuer(property.getIssuer())
                .setIssuedAt(localDateTimeToDate(nowTime))
                .setExpiration(getExpirationDate(nowTime, property.getExpiry()))
                .signWith(SignatureAlgorithm.HS256, property.getBase64Security());
        return builder.compact();
    }

    /**
     * 解析Token
     * +
     */
    public static Claims parseToken(String jsonWebToken, JwtProperty property) throws JwtException {
        return Jwts.parser()
                .setSigningKey(property.getBase64Security())
                .parseClaimsJws(jsonWebToken)
                .getBody();
    }

    /**
     * token刷新
     */
    public static String refreshToken(String oldToken, JwtProperty property) {
//        //token持续时间/分钟
//        long tokenDurationTime = property.getExpiry();
        //token允许刷新时间/分钟
        long tokenRefreshDurationTime = property.getRefresh();

        try {
            getExpirationDate(oldToken, property);
        } catch (ExpiredJwtException e) {
            /* 超时后刷新token */
            try {
                Claims claims = e.getClaims();
                long expirationTime = TimeUnit.MINUTES.convert(claims.getExpiration().toInstant().getEpochSecond(), TimeUnit.SECONDS);
                long nowTime = TimeUnit.MINUTES.convert(Instant.now().getEpochSecond(), TimeUnit.SECONDS);
                long tokenTimeout = nowTime - expirationTime;

                /* 允许范围内刷新，返回新token */
                if (tokenTimeout <= tokenRefreshDurationTime) {
                    return createToken(claims, claims.getSubject(), property);
                } else {
                    throw new GenericException(GenericErrorCode.TOKEN_TIMEOUT_ERROR);
                }
            } catch (JwtException je) {
                je.printStackTrace();
                throw new GenericException(GenericErrorCode.TOKEN_VALID_ERROR);
            } catch (Exception ex) {
                ex.printStackTrace();
                throw new GenericException(ex, GenericErrorCode.TOKEN_REFRESH_ERROR);
            }
        }
        return null;
    }


    /**
     * 获取token过期时间
     */
    private static Date getExpirationDate(String token, JwtProperty property) throws ExpiredJwtException {
        Claims claims = parseToken(token, property);
        Date expiration = claims.getExpiration();
        return expiration;
    }

    /**
     * 将LocalDateTime转换为Date
     */
    private static Date localDateTimeToDate(LocalDateTime localDateTime) {
        ZoneId zoneId = ZoneId.systemDefault();
        ZonedDateTime zdt = localDateTime.atZone(zoneId);
        return Date.from(zdt.toInstant());
    }

    /**
     * 计算过期时长
     */
    private static Date getExpirationDate(LocalDateTime createTime, long calendarInterval) {
        LocalDateTime expirationDate = createTime.plus(calendarInterval, ChronoUnit.MINUTES);
        return localDateTimeToDate(expirationDate);
    }

}
